package com.suxin.event.annotations;

import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;

/**
 * @author Tang
 * @classname EventListenerMethodProcessor
 * @description [  ]
 * @date 2023/2/28 16:22
 */
@Component
public class EventListenerMethodProcessor implements ApplicationContextAware, SmartInitializingSingleton, BeanFactoryPostProcessor {

    private AnnotationConfigApplicationContext applicationContext;

    private Collection<EventListenerFactory> listenerFactories;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 获取所有的 EventListenerFactory
        listenerFactories = beanFactory.getBeansOfType(EventListenerFactory.class).values();
    }

    @Override
    public void afterSingletonsInstantiated() {
        final String[] names = this.applicationContext.getBeanNamesForType(Object.class);
        for (String name : names) {
            final Class<?> targetClass = AutoProxyUtils.determineTargetClass(applicationContext.getBeanFactory(), name);
            // 处理所有的bean
            processBean(name,targetClass);
        }
    }

    private void processBean(String name, Class<?> targetClass) {
        if (AnnotationUtils.isCandidateClass(targetClass, EventListener.class)) {
            final Map<Method, EventListener> methods = MethodIntrospector.selectMethods(targetClass,
                    (MethodIntrospector.MetadataLookup<EventListener>) method ->
                            AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
            // 注册 ApplicationListener
            for (Method method : methods.keySet()) {
                for (EventListenerFactory listenerFactory : listenerFactories) {
                    if (listenerFactory.supportsMethod(method)) {
                        applicationContext.addApplicationListener(
                                listenerFactory.createApplicationListener(method,name,targetClass)
                        );
                    }
                }
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = (AnnotationConfigApplicationContext) applicationContext;
    }
}
